{# Layout macros: the pieces that frame a page rather than fill it. Import what you need: {% from "components/macros/layout.html" import card, stat_tile, dialog, dropdown %} #} {% from "components/macros/feedback.html" import empty_state %} {% from "components/macros/form.html" import action %} {# Navigation icons share one outline style. Unknown plugin entries use the four-square icon, so every installed item has an icon. #} {% macro sidebar_icon(name) %} {% set paths = { "overview": "M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6H9v6H4a1 1 0 0 1-1-1v-9.5Z", "patterns": "M4 4h6v6H4z M14 4h6v6h-6z M4 14h6v6H4z M17 14v6 M14 17h6", "secrets": "M15.5 7.5a3.5 3.5 0 1 1-4.95 3.18L3 18.25V21h2.75v-1.75h1.75V17.5h1.75l1.6-1.6A3.5 3.5 0 0 1 15.5 7.5Z M16.5 10.5h.01", "settings": "M4 6h16 M4 12h16 M4 18h16 M9 4v4 M15 10v4 M7 16v4", "backend": "M4 4h16v7H4z M4 13h16v7H4z M8 7.5h.01 M8 16.5h.01 M12 7.5h6 M12 16.5h6", "web_frontend": "M3 4h18v16H3z M3 9h18 M6 6.5h.01 M9 6.5h.01 M12 6.5h.01", "frontend": "M3 4h18v14H3z M8 21h8 M12 18v3", "database": "M4 6c0-2 16-2 16 0v12c0 2-16 2-16 0V6z M4 6c0 2 16 2 16 0 M4 12c0 2 16 2 16 0", "worker": "M12 3v3 M12 18v3 M3 12h3 M18 12h3 M5.6 5.6l2.1 2.1 M16.3 16.3l2.1 2.1 M18.4 5.6l-2.1 2.1 M7.7 16.3l-2.1 2.1 M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z", "scheduler": "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z M12 7v5l3 2", "cache": "M13 2 5 13h6l-1 9 9-12h-6l1-8Z", "storage": "M4 4h16v4H4z M5 8v12h14V8 M10 12h4", "ingress": "M3 7h16 M15 3l4 4-4 4 M21 17H5 M9 13l-4 4 4 4", "observability": "M3 20V4 M3 20h18 M6 15l4-4 3 2 5-6 M16 7h2v2", "ollama": "M12 2l2.2 6.8L21 11l-6.8 2.2L12 20l-2.2-6.8L3 11l6.8-2.2L12 2z M19 18v4 M17 20h4", "auth": "M5 10h14v11H5z M8 10V7a4 4 0 0 1 8 0v3 M12 14v3", "ai": "M12 2l2.2 6.8L21 11l-6.8 2.2L12 20l-2.2-6.8L3 11l6.8-2.2L12 2z M19 18v4 M17 20h4", "comms": "M4 4h16v12H9l-5 4V4z M7 8h10 M7 12h7", "documents": "M6 3h9l4 4v14H6z M15 3v5h4 M9 12h7 M9 16h7", "insights": "M3 20h18 M5 17v-5h3v5 M11 17V7h3v10 M17 17V4h3v13", "payment": "M3 5h18v14H3z M3 10h18 M7 15h4", "finance": "M12 3c5 0 9 2 9 4s-4 4-9 4-9-2-9-4 4-4 9-4z M3 7v5c0 2 4 4 9 4s9-2 9-4V7 M3 12v5c0 2 4 4 9 4s9-2 9-4v-5", "blog": "M5 4h11l3 3v13H5z M15 4v4h4 M8 12h8 M8 16h5", "default": "M3 3h7v7H3z M14 3h7v7h-7z M3 14h7v7H3z M14 14h7v7h-7z" } %} {% endmacro %} {# A titled surface. The body comes via {% call %}; the heading id ties the section to its title for assistive tech. ``id`` lets a page target the card with htmx; it defaults to a slug of the title. #} {% macro card(title=None, subtitle=None, id=None) %} {% set card_id = id or ("card-" ~ title|lower|replace(" ", "-")) %}
{# A card whose page title already names it (the forecast's chart) goes without a header rather than repeating the words. #} {% if title %}

{{ title }}

{% if subtitle %}

{{ subtitle }}

{% endif %}
{% endif %} {{ caller() }}
{% endmacro %} {# One headline figure. Render a row of them inside a ``
``. Red once a figure goes negative and nothing otherwise: when every healthy number is coloured, colour stops meaning anything. #} {% macro stat_tile(label, value, negative=False, caption=None, attrs="") %}
{{ label }}
{{ value }}
{% if caption %}
{{ caption }}
{% endif %}
{% endmacro %} {# A thin native progress bar; ``tone`` colours it (see badge). #} {% macro progress(ratio, tone="ok", label=None, size="h-1") %} {% set tones = {"ok": "text-aegis-teal", "warn": "text-aegis-amber", "error": "text-aegis-error", "muted": "text-aegis-muted"} %} {% endmacro %} {# A reusable current-use meter. The caller supplies the resource's detail text; health thresholds already determine the metric's status and tone. #} {% macro resource_bar(label, metric, detail="") %} {% set percent = metric.metadata.get("percent_used") if metric else none %}
{{ label }} {% if percent is number %}{{ "%.1f"|format(percent) }}% {% else %}Checking…{% endif %}
{% if percent is number %} {% set tone = metric.status.value|health_tone %} {{ progress(percent / 100, tone, label ~ " usage", "h-2") }} {% else %}{% endif %} {% if detail and percent is number %}

{{ detail }}

{% endif %}
{% endmacro %} {# A chart card (JS island #1). The canvas declares its kind and points at a JSON data block; static/js/charts.js mounts Chart.js over every ``canvas[data-chart]`` on load and after each htmx settle. Data shape: ``{"labels": [...], "series": [{"label": ..., "values": [...]}], "slices": [{"categories": [...]}], "format": "money"}`` (``slices`` only for drilldowns; numbers are plain unless ``format`` is ``money``). ``drilldown`` is the URL a slice click loads into the dialog; the slice's categories are appended as ``category=`` params. #} {% macro chart_panel(id, title, kind, data, drilldown=None, subtitle=None) %} {# ``title`` may be ``None``: the canvas keeps its aria-label either way. #} {% call card(title, subtitle, id="chart-" ~ id) %}
{% endcall %} {% endmacro %} {# The one modal. Native : focus trap, Escape and backdrop come free. Content is loaded by htmx into ``#dialog-body`` (pattern 4); app.js opens the dialog when a swap lands there, and closing clears it so a stale body never flashes on the next open. Mounted once by the app shell, outside ``#app-content``. #} {% macro dialog() %}
{% endmacro %} {# A brand mark before a name: the icon on a light tile when there is one (served from a cached URL, not inlined, so the browser fetches it once), else the initial letter. The letter rides a data attribute, not text, so the cell's text stays just the name. The tile is light in every theme: plenty of logos are drawn dark on transparency, invisible on a dark surface. #} {# ``size``: ``xs`` a footer's tiny tile, ``sm`` a table row's, ``lg`` a group heading's. Each is (tile, mark, letter, mark px), spelled out so Tailwind sees every class. #} {% set _avatar_sizes = { "xs": ("w-3.5 h-3.5", "w-2.5 h-2.5", "text-[8px]", 10), "sm": ("w-5 h-5", "w-3.5 h-3.5", "text-xxs", 14), "lg": ("w-7 h-7", "w-5 h-5", "text-xs", 20), } %} {% macro avatar(name, icon_url=None, size="sm") %} {% set tile, mark, letter, px = _avatar_sizes[size] %} {% if icon_url %} {% else %} {% endif %} {% endmacro %} {# The one drawer: one item of a list, sliding in from the right over the page. Non-modal, so the list stays clickable and rows can be walked without closing it. A list says which item is open with ``drawer_sync`` and app.js does the rest; mounted once by the app shell, beside the modal. #} {# The side drawer. Opened non-modally (it sits beside the page), so it stacks by z-index: above the chat drawer's overlay (z-50), which a reply's agent settings open from. #} {% macro drawer() %}
{% endmacro %} {# Which drawer a list has open: ``url`` loads into it, or none closes it. ``param`` is the query parameter that holds the open item, dropped from the address when the drawer is closed. #} {% macro drawer_sync(url, param) %}{% endmacro %} {# Steward's theme and mode chooser, shared by navbars and auth pages. #} {% macro theme_toggle(placement="down", rail=False) %}
{% if rail %}Appearance{% else %}Appearance{% endif %}
{% for key, legend, choices in [("theme", "Theme", [("aegis", "Aegis"), ("steward", "Steward")]), ("mode", "Mode", [("dark", "Dark"), ("light", "Light"), ("system", "System")])] %}
{{ legend }}
{% for value, label in choices %} {% endfor %}
{% endfor %}
{% endmacro %} {# Full-bleed dismiss layer behind a modal. Pair it with the modal panel:
{{ modal_scrim("close()") }}
...
#} {% macro modal_scrim(dismiss_handler="open = false") %}
{% endmacro %} {# THE tooltip panel — the one popover shell every hover/help hint uses. Callers provide the open/show()/hide() Alpine state on a ``relative`` wrapper and pass the body via {% call %}. Change tooltip styling here, nowhere else. #} {% macro popover_panel(align='left') %} {% endmacro %} {# Inline definition hint — wrap a term via {% call %}; hovering (or tapping) opens the shared popover panel. Dotted underline signals the affordance without a "?" button. ``align='right'`` for terms near the right edge. #} {% macro hover_hint(title, text, align='left') %} {{ caller() }} {% call popover_panel(align) %}
{{ title }}
{{ text }}
{% endcall %}
{% endmacro %} {# Small ? info icon with a click-triggered popover. Body can be arbitrary HTML (caller passes it as the macro body via {% call %}). #} {% macro info_tooltip(width="w-64") %}
{{ caller() }}
{% endmacro %} {# A small status word with a dot: ``tone`` is ok, warn, error, muted or accent; no tone renders the label plain. One shape for every "state" cell and chip, so tests find them by role. #} {% macro badge(label, tone=None) %} {% set tones = {"ok": "text-aegis-teal", "warn": "text-aegis-amber", "error": "text-error", "muted": "text-aegis-muted", "accent": "text-aegis-accent"} %} {% if tone %}{% endif %}{{ label }} {% endmacro %} {# One entry in a dropdown() menu. ``attrs`` carries the htmx verb (``hx-get``/``hx-post``/``hx-delete`` plus target and swap); the menu closes itself on the click, so a dialog never opens over it. #} {% macro menu_item(label, attrs, danger=False) %}
  • {% endmacro %} {% macro dropdown(label, align="right") %}
    {{ label }}
    {% endmacro %} {# A confirmation as dialog body: what will happen, one destructive action (``method`` is the htmx verb for ``url``: delete, post, patch; ``attrs`` rides on its button; ``tone`` is the ``action`` tone, danger unless the action is not destructive), and a way out. The action's response closes the dialog and navigates or swaps as it sees fit; nothing swaps here (hx-swap="none"). #} {# A dialog's heading: what this is, and the one line under it. ``subtitle`` is a sentence - what the dialog is for, or what it will do. ``facts`` is a metadata line (kind, date, size) and is set in the label voice instead. A ``{% call %}`` block supplies that line itself when it carries markup. The margin belongs here because it depends on whether anything follows: a title with a line under it sits close to it, a title alone stands off from the form below. Written out at each dialog it drifts - downstream it had reached four different answers across thirty-five. #} {% macro dialog_title(title, subtitle=None, facts=None) %} {%- set under = subtitle or facts or caller is defined %}

    {{ title }}

    {%- if facts %}

    {{ facts }}

    {%- elif subtitle %}

    {{ subtitle }}

    {%- elif caller is defined %} {{ caller() }} {%- endif %} {% endmacro %} {% macro confirm(title, body, url, label="Remove", method="delete", attrs="", tone="danger") %} {{ dialog_title(title, body) }}
    {% set verb %}hx-{{ method }}="{{ url }}" hx-swap="none" {{ attrs|safe }}{% endset %} {{ action(label, verb, tone) }}
    {% endmacro %} {# The page header, dashboard shape: title and subtitle on the left, and whatever the caller puts on the right (headline ``figures``, chips, actions) in the same row. Every section opens with one. #} {% macro page_header(title, subtitle=None) %}

    {{ title }}

    {% if subtitle %}

    {{ subtitle }}

    {% endif %}
    {{ caller() if caller is defined }}
    {% endmacro %} {# Headline figures for a header: label over a large number, right-aligned. ``items`` are ``{"label", "value", "negative"}``; ``negative`` colours the number, nothing else does. #} {% macro figures(items) %}
    {% for item in items %}
    {{ item.label }}
    {{ item.value }}
    {% endfor %}
    {% endmacro %} {# One bordered strip of N cells (the Budget's month equation). Each cell is ``{"label", "value", "caption", "tone", "attrs"}``; ``attrs`` makes it a door (hx-get) and ``tone`` colours the value. #} {% macro stats_strip(cells, id=None, attrs="") %} {# ``lg:grid-cols-N`` is looked up, never interpolated: Tailwind emits the classes it can SEE in the markup, and a class built at render time is not one of them - ``lg:grid-cols-{{ cells|length }}`` compiled to nothing and every strip stayed two columns at every width. #}
    {% for cell in cells %}
    {# A figure that can explain itself has to LOOK like it does: the hover fill is only found by the mouse already on it. The mark is for ``explain`` alone, not for any clickable cell - a cell that filters a list is not offering to define a word. #}
    {{ cell.label }} {% if cell.explain %}{% endif %}
    {{ cell.value }}
    {% if cell.caption %}
    {{ cell.caption }}
    {% endif %}
    {% endfor %}
    {% endmacro %} {# Ranked rows: name, a small count, the amount, and a thin bar scaled to the largest row (the Overview's payees and bills). ``rows`` are ``{"label", "count", "value", "ratio", "tone"}``. #} {% macro ranked_rows(rows) %}
      {% for row in rows %}
    1. {{ row.label }} {% if row.count %}{{ row.count }}{% endif %}{{ row.value }}
    2. {% endfor %}
    {% endmacro %} {# Tabs: sibling views of one page, Pulse's underline recipe (the same look Flet's tabs had). ``tab_bar`` is the row (``role="tablist"``); each ``tab_item`` is a link: ``href`` for a cold load, ``hx`` the htmx attributes (``hx_replace(...)`` or a plain string). #} {% macro tab_bar(label, id=None, attrs="") %} {% endmacro %} {% macro tab_item(label, active=False, href="", hx="") %} {{ label }} {% endmacro %} {# A chip: one of a row of exclusive choices (a time window, a month, a view). Pulse's date-range recipe, which the Flet range chips copied: small, outlined, a light teal fill when chosen. #} {% macro chip(label, active=False, href="", hx="", extra="") %} {{ label }} {% endmacro %} {# Copy ``value`` to the clipboard and confirm with a toast. The value rides in a data attribute, so it is escaped like any other markup. #} {% macro copy_button(value, label="Copy") %} {# app.js copies it (the one copier, with the plain-http fallback). #} {% endmacro %} {# A block of source (SQL, Python) with a copy button. ``caption`` is a line above it, such as the file it came from. ``lang`` renders it as a fenced block through the ``markdown`` filter, so it is highlighted for that language; the copy button always takes the plain text. #} {% macro code_block(text, caption=None, lang=None) %}

    {{ caption or "" }}

    {{ copy_button(text) }}
    {% if lang %}
    {{ ("````" ~ lang ~ "\n" ~ text ~ "\n````")|markdown }}
    {% else %}
    {{ text }}
    {% endif %}
    {% endmacro %} {# Label/value rows: a component's status, an expanded row's details. ``rows`` are (label, value) pairs; empty values are skipped. #} {% macro facts(rows) %}
    {% for label, value in rows if value not in (none, "", [], {}) %}
    {{ label }}
    {{ value|docstring if value is string else value|join(", ") if value is iterable and value is not mapping else value }}
    {% endfor %}
    {% endmacro %} {# A wrapping row of label-over-value figures, the Flet detail row. ``items`` are (label, value) pairs. #} {% macro stat_row(items) %}
    {% for label, value in items %}
    {{ label }}
    {{ value }}
    {% endfor %}
    {% endmacro %}